home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
090
/
cmln0286.arc
/
STATECOM.C
< prev
next >
Wrap
C/C++ Source or Header
|
1986-02-03
|
17KB
|
723 lines
/*
* statecom.c - state machine preprocessor
* as described in february 1986 computer language magazine
*
* compile options
* -dNOVOID = no void type supported by compiler
* -dBITFIELDS = bitfields are supported by compiler
* -dAZTEC86 = the perror library routine is used rather than homebrew,
* should be otherwise self contained
*/
char *version = "statecom version 1.0\n";
#include <stdio.h>
#include <ctype.h>
#include <debug.h>
/* for those of you without void type */
#ifdef NOVOID
typedef int void;
#endif
/* for those of you without perror() */
#ifndef AZTEC86
void perror(s)
char *s;
{
extern int errno;
fprintf(stderr,"%s : system error %d\n",s,errno);
}
#endif
#define FALSE 0
#define TRUE 1
#define fileclose(f) {if (-1==fclose(f)) perror("fclose");}
#define filekill(f) {if (-1==unlink(f)) perror("unlink");}
#define freemem(m) {if (-1==free(m)) perror("free");}
#define usizeof(x) ((unsigned)sizeof(x))
/*
* external functions
*/
FILE *fopen();
char *savestr();
char *fgets();
char *index();
void *malloc();
char *strcpy(),*strcat();
void perror();
/*
* forward definition of process to void
*/
void process();
void copytilblank();
/*
* structure of state compiler symbol table entries
*/
typedef struct s
{
struct s *next;
char *statename; /* name of state */
#ifdef BITFIELDS
unsigned defined:1; /* defined flag */
unsigned referenced:1; /* referenced */
#else
char defined;
char referenced;
#endif
} statedesc, *stateptr;
/*
* global variables
*/
FILE *input, /* input file */
*output; /* output file */
statedesc statetable; /* head of state compiler symbol table */
char currname[64]; /* name of current input file */
char outname[64] /* name of current output file */
= "stateout.c"; /* defaults to stateout.c */
char *inptr; /* pointer into current input line */
int inlineno; /* line number in input file */
char buffer[256]; /* contains current input file line */
char verbose = 0; /* whether or not to dump table at end */
/* state machine compiler keywords */
static char *
keywords[] =
{
"machine",
"state",
"endstate",
"endmachine",
"nextstate",
"debug",
"nodebug",
"endargs"
};
/* symbolic constants for indexes into keyword array */
#define MACHINE 0
#define STATE 1
#define ENDSTATE 2
#define ENDMACHINE 3
#define NEXTSTATE 4
#define SDEBUG 5
#define SNODEBUG 6
#define ENDDECL 7
/* symbolic constant for size of keyword array */
#define KEYSIZE (usizeof(keywords)/usizeof(char *))
/*
* main function
*/
main(argc,argv)
char *argv[];
{
char *dotptr;
puts(version);
if (argc == 1)
{
strcpy(currname,"statein.s");
process(stdin);
}
else
{
while (--argc)
{
++argv;
if ((*argv)[0] == '-')
{
char *opt = &(*argv)[1];
while (*opt)
{
switch (*opt)
{
case 'v':
case 'V':
verbose = 1;
break;
default:
fprintf(stderr,"unrecognized option %c\n",*opt);
}
opt++;
}
continue; /* look for another argument */
}
strcpy(currname,*argv);
strcpy(outname,currname); /* copy to output name */
/* if no extension is specified assume .s
*/
if (NULL == (dotptr = index(currname,'.')))
{
strcat(currname,".s");
}
if (NULL == (input = fopen(currname, "r")))
{
perror("statecom");
continue;
}
/* blithely overwrite corresponding
C source file
*/
if (NULL == (dotptr = index(outname,'.')))
{
strcat(outname,".c");
}
else
{
++dotptr;
*dotptr++ = 'c';
*dotptr = '\0'; /* probably unnecessary */
}
/* initialize state table variable */
statetable.next = NULL;
statetable.statename = "";
statetable.referenced = FALSE;
statetable.defined = FALSE;
process(input);
reset_parser();
}
}
}
/*
* newstate - enters a new state name into symbol table.
* returns a pointer to the new symbol
*/
stateptr
newstate(name)
char *name;
{
register stateptr current;
if (NULL == (current = (stateptr)malloc(usizeof(statedesc))))
{
perror("statecom");
exit(1);
}
current->statename = savestr(name);
current->next = statetable.next;
statetable.next = current;
current->defined = FALSE;
current->referenced = FALSE;
return current;
}
/*
* reset_parser
* clears out all symbol table allocations to start on new file
*/
reset_parser()
{
register stateptr kill = statetable.next;
register stateptr tmp;
while (kill)
{
tmp = kill;
kill = kill->next;
/*lint -e516 */
freemem(tmp->statename);
freemem(tmp);
/*lint +e516 */
}
statetable.next = NULL;
}
/*
* print out the symbol table
*/
void dump_table()
{
void print_state();
register stateptr next = statetable.next;
while(next)
{
print_state(next);
next = next->next;
}
}
/*
* print out information about a state
*/
void print_state(state)
register stateptr state;
{
fprintf(stderr,"%s %s %s\n",state->statename,
state->defined ? "defined" : "undefined",
state->referenced ? "referenced" : "unreferenced" );
}
/*
* instatetable
* returns pointer to symbol table entry matching name,
* or NULL if none is found
*/
stateptr
instatetable(name)
char *name;
{
register stateptr current = statetable.next;
while(current)
{
if (0 == strncmp(current->statename,name,strlen(name)))
return current;
current = current->next;
}
return NULL;
}
/*
* cleanup
* deletes all open files for a fatal error abort
*/
void
cleanup() /* get rid of temporary files */
{
fileclose(output);
filekill(outname);
exit(1);
}
/*
* serror
* prints out msg, plus the current input line
*/
serror(msg,fatal)
char *msg;
{
fprintf(stderr,"line %d : %s\n%s\n", inlineno,buffer,msg);
if (fatal)
{
dump_table();
cleanup();
}
}
/*
* process
* steps through an input file, and generates a c source file
*/
void
process(infile)
FILE *infile;
{
register int i; /* good old loop counter i */
char machinename[64];
char margs[64];
char statename[64];
char targetname[64];
char *skipblanks(),*skipnonblanks(),*nexttoken();
stateptr currstate;
int inmachine = FALSE, instate = FALSE;
int indeclaration = FALSE;
if (NULL == (output = fopen(outname,"w")))
{
perror("statecom");
exit(1);
}
inlineno = 0; /* line incremented after reading each line */
/* put out a #line directive at top of file */
fprintf(output,"#line 1 \"%s\"\n",currname);
/* make pass through the source file */
while (NULL != fgets(buffer,sizeof(buffer),infile))
{
inlineno++;
if (NULL == (inptr = skipblanks(buffer)) || (*inptr != '$') )
/* if just white space or if first non blank character isn't $ */
{
/* pass thru unmodified */
fputs(buffer,output);
DEBUGGER(fprintf(stderr,"%sno $ seen\n",buffer););
continue;
}
DEBUGGER(fprintf(stderr,"%s$ seen\n",buffer););
/* otherwise we have a command to process */
++inptr; /* point past $ */
for (i = 0; i <= KEYSIZE ; i++)
{
if (0 == strncmp(keywords[i],inptr,strlen(keywords[i])))
{
switch(i)
{
case MACHINE:
if (inmachine)
{
/* assume we just forgot an end of state */
serror("nested state machines",FALSE);
emitmend(machinename);
}
inmachine = TRUE; /* in a state machine */
indeclaration = TRUE; /* waiting for first state */
/* skip forward to first parm */
inptr = nexttoken(inptr);
/* copy name to machine name */
copytilblank(machinename,inptr);
if (instatetable(machinename))
/* may or may not work, depends on compiler, assume fatal */
serror("machine name conflicts with state name",TRUE);
/* advance inptr to next string */
inptr = nexttoken(inptr);
/* copy name to state name */
copytilblank(statename,inptr);
/* advance inptr to arg list */
inptr = nexttoken(inptr);
/* copy rest of line to argument clause */
strcpy(margs,inptr);
/* add state to table if necessary */
if (NULL == instatetable(statename))
{
currstate = newstate(statename);
currstate->referenced = TRUE;
currstate->defined = FALSE;
}
emitmachine(machinename,margs);
break;
case ENDDECL:
if (instate)
{
serror("Bad place for $enddecl",FALSE);
break; /* fall out of switch without making a mess */
}
if (!inmachine)
/* would cause bad code */
serror("Not in machine",TRUE);
fputs("{\n",output);
fprintf(output,"#line %d \"%s\"\n",inlineno+1,currname);
break;
case STATE:
if (instate)
{
/* assume end of state was just left out */
serror("Nested states",FALSE);
emitendstate(statename);
}
if (!inmachine)
/* would generate bad code */
serror("Not in machine",TRUE);
instate = TRUE;
if (indeclaration)
{
indeclaration = FALSE;
emitdeclend(machinename,statename);
}
inptr = nexttoken(inptr);
/* copy token to state name */
copytilblank(statename,inptr);
/* check to see if its defined */
if (0 != (currstate = instatetable(statename)))
{
if (currstate->defined)
/* multiply defined goto labels wouldn't work */
serror("Multiply defined state",TRUE);
else
; /* referenced but not defined */
}
else
{
/* add to table */
currstate = newstate(statename);
}
/* we're defining it here */
currstate->defined = TRUE;
emitstate(statename);
break;
case ENDSTATE:
if (!instate)
{
/* just ignore this, for argument's sake */
serror("In no state",FALSE);
break;
}
if (!inmachine)
{
/* just ignore */
serror("Not in machine",FALSE);
break;
}
instate = FALSE;
inptr = nexttoken(inptr);
copytilblank(targetname,inptr);
if (0 !=
strncmp(statename,targetname,strlen(statename)))
/* picky, picky picky! */
serror("$endstate doesnt match $state",FALSE);
emitendstate(targetname);
break;
case ENDMACHINE:
if (instate)
{
serror("Unterminated state",FALSE);
emitendstate(statename);
}
if (!inmachine)
serror("Not in machine",TRUE);
inmachine = FALSE;
inptr = nexttoken(inptr);
copytilblank(targetname,inptr);
if (0 !=
strncmp(machinename,targetname,strlen(machinename)))
serror("$endmachine doesn't match $machine",FALSE);
emitmend(machinename);
break;
case NEXTSTATE:
if (!instate)
{
/* just ignore */
serror("Not in state",FALSE);
break;
}
if (!inmachine)
{
/* just ignore */
serror("Not in machine",TRUE);
break;
}
inptr = nexttoken(inptr);
/* copy token to state name */
copytilblank(targetname,inptr);
/* terminal is a 'meta-state', and doesn't ever get
put into state table
*/
if (0 != strcmp("terminal",targetname))
{
/* check to see if its defined */
if (NULL == (currstate = instatetable(targetname)))
{
/* add to table */
currstate = newstate(targetname);
}
/* set it referenced */
currstate->referenced = TRUE;
}
fprintf(output,"/* $ */ goto %s;\n",targetname);
break;
case SDEBUG:
fputs(
"\n#ifdef SNODEBUG\n#undef SNODEBUG\n#endif\n",output);
fprintf(output,"#line %d \"%s\"\n",inlineno,currname);
break;
case SNODEBUG:
fputs("\n#ifndef SNODEBUG\n#define SNODEBUG\n#endif\n",
output);
fprintf(output,"#line %d \"%s\"\n",inlineno,currname);
break;
};
break; /* once we find one keyword don't look for more */
}
}
/* if we didn't find key words, a stray $ at beginning of line */
if (i == KEYSIZE+1)
serror("No keyword recognized\n",FALSE);
}
/* check for stupid errors */
if (instate)
{
fprintf(stderr,"End of file\nunterminated state %s\n" ,statename);
cleanup();
}
if (inmachine)
{
fprintf(stderr,"End of file\nunterminated machine %s\n",machinename);
cleanup();
}
currstate = statetable.next;
while (currstate)
{
if (currstate->referenced && !currstate->defined)
{
fprintf(stderr,"ERROR: state %s referenced but not defined\n",
currstate->statename);
cleanup();
}
if (currstate->defined && !currstate->referenced)
{
fprintf(stderr,"WARNING: state %s defined but not referenced\n",
currstate->statename);
}
currstate = currstate->next;
}
if (verbose)
dump_table();
fprintf(stderr,"Done\n");
fileclose(output);
}
/*
* emitmachine
* outputs the main state machine function, with argument list
*/
emitmachine(machinename,argclause)
char *machinename,*argclause;
{
fprintf(output,"\n/*\n * statemachine %s\n */\n",machinename);
fprintf(output,"%s %s\n",machinename,argclause); /* first line */
fprintf(output,"#line %d \"%s\"\n",inlineno+1,currname);
}
emitmend(machinename)
char *machinename;
{
fputs("\n/*\n * BAD STATE LABEL\n */\nbadstate:\n",output);
fputs("\n\tfprintf(stderr,",output);
fputs("\"Fallen off end of a state!!!\\n\");\n"
,output);
fputs("\n\treturn -1;\n",output);
fputs("\n/*\n * TERMINAL STATE LABEL\n */\nterminal:\n",
output);
fputs("\n\treturn 0;\n",output);
fprintf(output,
"\n/*\n * end of state machine %s\n */\n}\n"
,machinename);
}
/*
* emitstate
* emits a function declaration and opening brace
*/
emitstate(statename)
char *statename;
{
fprintf(output,"\n%s:\n",statename); /* first line */
fputs("{\n",output);
fprintf(output,"#line %d \"%s\"\n",inlineno+1,currname);
}
/*
* emitendstate
* emits a function termination clause
*/
emitendstate(statename)
char *statename;
{
/* last line (call to badstate) */
fputs("\n#ifndef SNODEBUG\n",output);
fputs("goto badstate;\n",output);
fputs("#endif\n",output);
fprintf(output,"/* \n * $endstate %s\n */\n};\n",statename);
fprintf(output,"#line %d \"%s\"\n",inlineno+1,currname);
}
/*
* emit the end of machine local variables declarations
*/
emitdeclend(machinename,statename)
char *machinename, *statename;
{
fprintf(output,"\n/*\n * end of declarations for %s\n */\n",machinename);
fprintf(output,"/* $ */ goto %s;\n",statename);
fprintf(output,"#line %d \"%s\"\n",inlineno+1,currname);
}
/*
* savestr
* make a copy of a string to dynamically allocated memory
* and return a pointer to it
*/
char *
savestr(s)
register char *s;
{
register char *r;
/* squirrel away matched file name */
if (NULL == (r = malloc((unsigned)(strlen(s)+1))))
serror("Out of memory",TRUE);
strcpy(r,s);
r[strlen(s)] = '\0';
return r;
}
/* nexttoken
* inptr points at start of a token string
* nexttoken advances it to the start of the next token string
*/
char *
nexttoken(string)
register char *string;
{
char *skipblanks(); char *skipnonblanks();
/* point past keyword */
if (NULL == (string = skipnonblanks(string)))
errorout:
serror("missing keyword argument",TRUE);
if (NULL == (string = skipblanks(string)))
goto errorout;
return string;
}
/*
* skipblanks
* returns pointer to first non-blank character in string
* or null if end of string is found
*/
char *
skipblanks(string)
register char *string;
{
while (isspace(*string))
++string;
if (*string == '\0')
return NULL;
return string;
}
/*
* skipnonblanks
* returns pointer to first blank character in string
* or null if end of string is found
*/
char *
skipnonblanks(string)
register char *string;
{
while ( !isspace(*string) )
++string;
if (*string == '\0')
return NULL;
return string;
}
/*
* copytilblank
* copy from source to target until a blank is found
*/
void copytilblank(target,source)
char target[];
register char *source;
{
register int i;
/* copy name to target name */
for(i = 0; *source && !isspace(*source); i++)
target[i] = *source++;
target[i] = '\0'; /* terminate name */
DEBUGGER(fprintf(stderr,"token = %s\n",target);)
}
ted machine %s\n",machinename);
cleanup();
}